package plus.modular.jfinal.web.controller;

import cn.hutool.setting.Setting;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.jfinal.core.Controller;
import com.jfinal.core.NotAction;
import com.jfinal.core.converter.TypeConverter;
import com.jfinal.kit.HttpKit;
import com.jfinal.kit.StrKit;
import com.jfinal.log.Log;
import plus.modular.common.util.MapUtil;
import plus.modular.jfinal.web.annotation.ModelField;
import plus.modular.jfinal.web.config.DefaultConfig;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Map;

/**
 * 基础控制层
 */
public abstract class BaseController extends Controller {

    /**
     * 分页模式(start 行模式 page 页模式)
     */
    private static String PAGING_MODE = "rows";
    /**
     * 查询的start开始标识
     */
    private static String PAGING_START_KEY = "start";
    /**
     * 查询的limit开始标识
     */
    private static String PAGING_LIMIT_KEY = "limit";

    private static Setting setting = null;

    static{
        try {
            setting = new Setting("config/jfinal.txt");
        } catch (Exception e) {
            Log.getLog(DefaultConfig.class).error("未找到 {} 配置文件，使用默认配置","config/jfinal.txt");
            setting = new Setting();
        }
    }

    @NotAction
    public boolean isPjaxRequest() {
        return "true".equalsIgnoreCase(getHeader("X-PJAX"));
    }

    /**
     * 判断是否为Ajax请求
     * @return
     */
    @NotAction
    public boolean isAjaxRequest() {
        if((getHeader("X-Requested-With") != null && getHeader("X-Requested-With").contains("XMLHttpRequest"))){
            if(getHeader("accept") == null || getHeader("accept").contains("application/json")){
                return true;
            }
        }
        if(getAttr("_dataType")!=null && getAttr("_dataType").equals("json")){
            return true;
        }
        return false;
    }

    /**
     * 从请求体中获得JSONObject对象
     * @return
     */
    @NotAction
    public JSONObject getJSONObjectByRequestBody(){
        return JSONObject.parseObject(HttpKit.readData(getRequest()));
    }

    /**
     * 从请求体中获得Map对象
     * @return
     */
    @NotAction
    public Map<String,Object> getMapByRequestBody(){
        return getJSONObjectByRequestBody().toJavaObject(Map.class);
    }

    /**
     * 从请求体中获得JSONArray对象
     * @return
     */
    @NotAction
    public JSONArray getJSONArrayByRequestBody(){
        return JSONArray.parseArray(HttpKit.readData(getRequest()));
    }

    /**
     * 获得Map参数
     * @return
     */
    @NotAction
    public Map<String,Object> getParams(){
        Map<String,Object> params = new HashMap<String,Object>();
        for (Map.Entry<String, String[]> entry : getRequest().getParameterMap().entrySet()) {
            if(entry.getValue()!=null){
                if(entry.getValue().length == 1){
                    params.put(entry.getKey(), entry.getValue()[0]);
                }else if(entry.getValue().length > 1){
                    params.put(entry.getKey(), entry.getValue());
                }
            }
        }
        return params;
    }

    /**
     * 获得分页参数
     * @return
     */
    @NotAction
    public Map<String,Object> getPageParams(){
        Map<String,Object> params = getParams();
        /**
         * 获取分页模式
         * rows 代表已传递参数为 start limit
         * page 代表传递的参数为 pageNo limit
         */
        if("rows".equals(PAGING_MODE)){
            params.put("start", MapUtil.getInt(params, PAGING_START_KEY,0));
            params.put("limit", MapUtil.getInt(params, PAGING_LIMIT_KEY,10));
        }else if("page".equals(PAGING_MODE)){
            /* 执行转换 (page - 1) * pageRows + 1, page * pageRows */
            int pageNo = MapUtil.getInt(params, PAGING_START_KEY,1);
            int limit = MapUtil.getInt(params, PAGING_LIMIT_KEY,10);
            params.put("start", (pageNo - 1) * limit);
            params.put("limit", limit);
        }
        return params;
    }


    @NotAction
    public String getIpAddress(){
        return getIpAddress(getRequest());
    }

    @NotAction
    public String getIpAddress(HttpServletRequest request) {
        String ipAddress = request.getHeader("x-forwarded-for");
        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("Proxy-Client-IP");
        }
        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getRemoteAddr();
            if (ipAddress.equals("127.0.0.1") || ipAddress.equals("0:0:0:0:0:0:0:1")) {
                // 根据网卡取本机配置的IP
                InetAddress inet = null;
                try {
                    inet = InetAddress.getLocalHost();
                } catch (UnknownHostException e) {
                    e.printStackTrace();
                }
                ipAddress = inet.getHostAddress();
            }
        }
        // 对于通过多个代理的情况，第一个IP为客户端真实IP,多个IP按照','分割
        if (ipAddress != null && ipAddress.length() > 15) { // "***.***.***.***".length()
            // = 15
            if (ipAddress.indexOf(",") > 0) {
                ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
            }
        }
        return ipAddress;
    }
    @NotAction
    public <T> T getModelByAnnotation(Class<T> modelClass){
        /**
         * 获得类中的所有set方法
         */
        Object bean = createInstance(modelClass);
        Method[] methods = modelClass.getMethods();
        /* 对所有方法进行匹配 */
        if(methods!=null){
            Map<String, String[]> parasMap = getParaMap();
            TypeConverter converter = TypeConverter.me();
            for(Method method : methods){
                /* 直接判断是否存在这个注解。注意！这个注解必须写在setXxx方法上！ */
                ModelField field = method.getAnnotation(ModelField.class);
                if(field != null){
                    String methodName = method.getName();
                    if (methodName.startsWith("set") == false || methodName.length() <= 3) {	// only setter method
                        continue;
                    }
                    Class<?>[] types = method.getParameterTypes();
                    if (types.length != 1) {						// only one parameter
                        continue;
                    }
                    if (parasMap.containsKey(field.field())) {
                        try {
                            String paraValue = getPara(field.field());
                            Object value = paraValue != null ? converter.convert(types[0], paraValue) : null;
                            method.invoke(bean, value);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
        return (T)bean;
    }


    private static <T> T createInstance(Class<T> objClass) {
        try {
            return objClass.newInstance();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}
